package com.changge.module.security.filters;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.changge.common.core.domain.response.Result;
import com.changge.common.core.utils.StringUtil;
import com.changge.common.security.context.SecurityContext;
import com.changge.common.security.domain.LoginUserDetails;
import com.changge.common.security.exception.SecurityErrorCode;
import com.changge.common.security.utils.JwtTokenUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;

import static com.changge.common.core.constant.HttpConst.*;

/**
 * Jwt登录授权过滤器
 *
 * @author zhangrongkang
 * @since 2023/3/13
 */
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    /**
     * Jwt处理Token工具类
     */
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    /**
     * 用户信息加载业务
     */
    @Autowired
    UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
//        log.info("----------------- 自定义token授权过滤器 begin -----------------");
        // 从请求中获取token
        String authHeader = request.getHeader(AUTHORIZATION);
        // 如果请求中存在token字段
        if (StringUtil.isNoneBlank(authHeader) && StringUtil.startsWith(authHeader, BEARER)) {
            // 截取token
            String token = authHeader.substring(BEARER.length()).trim();
            // 校验token是否合法并返回结果
            SecurityErrorCode errorCode = switch (jwtTokenUtil.validateToken(token)) {
                // token不合法
                case ILLEGAL -> SecurityErrorCode.USER_SIGN_EXCEPTION;
                // token过期
                case EXPIRE -> SecurityErrorCode.AUTHORIZED_EXPIRED;
                // 校验通过
                case ACCESS -> null;
            };
            // token校验不通过
            if (Objects.nonNull(errorCode)) {
                log.error("解析token出错: {}", errorCode);
//                log.info("----------------- 自定义token授权过滤器 end -----------------");
                render(response, errorCode);
                return;
            }
            // 解析出当前 token 中的用户并将用户信息设置到 SecurityContextHolder 中
//            log.info("加载用户信息...... ");
            // 解析出 token 中的唯一标识
            String traceId = jwtTokenUtil.getTraceIdFromToken(token);
            UserDetails user = userDetailsService.loadUserByUsername(traceId);
            if (Objects.isNull(user)) {
                log.error("系统不存在当前用户，拦截当前请求");
//                log.info("----------------- 自定义token授权过滤器 end -----------------");
                render(response, SecurityErrorCode.USER_LOGIN_EXPIRED);
                return;
            }
            LoginUserDetails userDetails = (LoginUserDetails) user;
//            log.info("当前进行鉴权的用户为：{}", userDetails.getUsername());
//            log.info("当前用户的角色标识为：{}", userDetails.getRoleMark());
            // 设置用户全局唯一标识
            SecurityContext.setTraceId(traceId);
            // 用户详细信息（userDetails）、空密码、用户权限（userDetails.getAuthorities()）来获取认证令牌
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            // 将认证令牌设置为当前线程的安全上下文中的认证对象
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
//        log.info("----------------- 自定义token授权过滤器 end -----------------");
        // 放行
        filterChain.doFilter(request, response);
    }

    /**
     * 定义返回对象体
     *
     * @param response  前端响应
     * @param errorCode 错误码枚举
     * @throws IOException IO异常
     */
    private void render(HttpServletResponse response, SecurityErrorCode errorCode) throws IOException {
        // 定义响应编码格式
        response.setCharacterEncoding(UTF_8);
        // 定义返回对象格式
        response.setContentType(APPLICATION_JSON);
        // 定义返回结果
        PrintWriter out = response.getWriter();
        out.write(new ObjectMapper().writeValueAsString(Result.failure(errorCode)));
        out.flush();
        out.close();
    }

}
